/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "HyperVProtectEngine.h"
#include <iostream>
#include <set>
#include "fileapi.h"
#include <windows.h>
#include <common/utils/Utils.h>
#include "common/uuid/Uuid.h"
#include "protect_engines/hyperv/resource_discovery/HyperVResourceAccess.h"
#include "repository_handlers/factory/RepositoryFactory.h"
#include "protect_engines/hyperv/utils/executor/WinCmdExector.h"
#include "volume_handlers/hyperv_volume/HyperVVolumeHandler.h"
#include "repository_handlers/vhd_handler/VhdHandler.h"

namespace {
const std::string NEW_VM_INFO_FILE = VirtPlugin::VIRT_PLUGIN_CACHE_ROOT + "new_vm.info";
const std::string TARGET_TYPE_SCVMM = "SCVMM";
const std::string TARGET_TYPE_CLUSTER = "Cluster";
const std::string TARGET_TYPE_HOST = "Host";
const std::string TARGET_TYPE_VM = "VM";
const std::string TARGET_TYPE_DISK = "Disk";
const std::string TARGET_TYPE_ADAPTER = "Adapter";
const std::string TARGET_TYPE_DIRECTORY = "Directory";
const std::string TARGET_TYPE_KEY = "targetType";
const std::string SCAN_REQUEST_ID_KEY = "ScanRequestId";
const std::string SCAN_FAILED = "Scan failed";
const std::string SCAN_REQUEST_KEY_PARENT_NAME = "parentName";
const std::string SCAN_REQUEST_KEY_PARENT_ID = "parentId";
const std::string VHDS_TYPE = "vhdset";
const std::string RESOURCE_SUBTYPE_CLUSTER = "HyperV.Cluster";
const std::string DEFAULT_CHECKPOINT_PREFIX = "databackup_";
const std::string ADD_TO_CLUSTER_KEY = "addToCluster";
const std::string ADD_TO_CLUSTER = "1";
const std::string DELETE_ORIGIN_VM_KEY = "deleteOriginVM";
const std::string DELETE_ORIGIN_VM = "1";
const int MAX_RETRY_TIME_10 = 10;
} // namespace

using namespace VirtPlugin;

namespace {
const std::string RESOURCE_ACCESS_SCRIPT_DIR = Module::EnvVarManager::GetInstance()->GetAgentHomePath() +
    "/DataBackup/ProtectClient/Plugins/VirtualizationPlugin/";
const std::string RESOURCE_ACCESS_EXECUTOR = "Executor";
const DWORD TIMEOUT_DEFAULT = 300;
const std::string RESOURCE_TYPE = "HyperV";
const std::string CMD_TYPE_LIST_DISK = "ListDisk";
const std::string CACHE_VM_INFO_FILE = VIRT_PLUGIN_CACHE_ROOT + "hyperv_vm.info";

/* HyperV label */
const std::string HYPERV_ATTACH_VOLUME_FAILED_LABEL = "virtual_plugin_hyperv_restore_job_attach_volume_failed_label";
const std::string HYPERV_ATTACH_VOLUME_SUCCESS_LABEL = "virtual_plugin_hyperv_restore_job_attach_volume_success_label";
const std::string HYPERV_POWER_ON_FAILED_LABEL = "virtual_plugin_hyperv_restore_job_power_on_vm_failed_label";
const std::string HYPERV_POWER_ON_SUCCESS_LABEL = "virtual_plugin_hyperv_restore_job_power_on_vm_success_label";
const std::string HYPERV_LACK_OF_SPACE_LABEL = "virtual_plugin_hyperv_backup_job_storage_lack_of_free_space_label";
const std::string ADD_TO_CLUSTER_SUCCESS_LABEL = "virtual_plugin_hyperv_restore_job_add_to_cluster_success_label";
const std::string ADD_TO_CLUSTER_FAILED_LABEL = "virtual_plugin_hyperv_restore_job_add_to_cluster_failed_label";
constexpr int64_t VIRTPLUGIN_HYPERV_QUERRY_VM_FAILED = 0x5E025E97;
}

namespace HyperVPlugin {

int32_t HyperVProtectEngine::GetRepoPathByType(RepositoryDataType::type type, std::string& repoPath)
{
    repoPath = "";
    if (m_jobHandle->GetStorageRepos().size() == 0) {
        ERRLOG("No repo found", m_taskId.c_str());
        return FAILED;
    }
    for (const auto &repo: m_jobHandle->GetStorageRepos()) {
        if (repo.repositoryType != type) {
            continue;
        }
        m_repoHandler = std::make_shared<Win32FileSystemHandler>();
        if (m_repoHandler == nullptr) {
            ERRLOG("Create repository handler failed.");
            return FAILED;
        }
        for (const auto &path: repo.path) {
            if (m_repoHandler->IsDirectory(path)) {
                repoPath = path;
                break;
            }
        }
    }
    if (repoPath.empty()) {
        ERRLOG("Get meta repo path failed.");
        return FAILED;
    }
    return SUCCESS;
}

template<typename T>
int32_t HyperVProtectEngine::Executor(const std::string &command, T &res, const Json::Value &param)
{
    LOGGUARD("");
    Utils::Param prm;
    prm.scriptDir = RESOURCE_ACCESS_SCRIPT_DIR;
    prm.timeout = TIMEOUT_DEFAULT;
    prm.cmdType = command;
    prm.scriptName = RESOURCE_ACCESS_EXECUTOR;
    prm.param = param;
    Json::Value result;
    HyperVPlugin::Utils::WinCmdExector execaller;
    int retRes = execaller.Execute(prm, result);
    Module::JsonHelper::JsonValueToStruct(result, res);
    INFOLOG("Run executor end, retRes=%d", retRes);
    return retRes;
}

void HyperVProtectEngine::InitRepoHandler()
{
    if (m_metaRepoHandler != nullptr) {
        return;
    }
    std::vector<AppProtect::StorageRepository> repos = m_jobHandle->GetStorageRepos();
    for (const auto &repo : repos) {
        if (repo.repositoryType == RepositoryDataType::META_REPOSITORY) {
            m_metaRepoHandler = std::make_shared<Win32FileSystemHandler>();
            m_metaRepoPath = repo.path[0];
            INFOLOG("Meta repo path is %s.", m_metaRepoPath.c_str());
        } else if (repo.repositoryType == RepositoryDataType::CACHE_REPOSITORY) {
            m_cacheRepoHandler = std::make_shared<Win32FileSystemHandler>();
            m_cacheRepoPath = repo.path[0];
            INFOLOG("Cache repo path is %s.", m_cacheRepoPath.c_str());
        }
    }
}

bool HyperVProtectEngine::UpdateVolSnapInfo(const VolumeInfo &curVol, SnapshotInfo &preSnapshotInfo)
{
    for (auto &preVolSnap : preSnapshotInfo.m_volSnapList) {
        if (curVol.m_diskIdentifier == preVolSnap.m_volUuid) {
            preVolSnap.m_snapshotWwn = curVol.m_path; // 此处将wwn定义为磁盘路径
            INFOLOG("Disk(%s) current disk path(%s)", curVol.m_diskIdentifier, curVol.m_path.c_str());
            return true;
        }
    }
    return false;
}

bool HyperVProtectEngine::UpdatePreSnapShotInfo(const std::list<VolumeInfo> &diskList, const std::string &preSnapFile)
{
    SnapshotInfo preSnapshotInfo;
    if (m_metaRepoHandler->Exists(preSnapFile)) {
        if (VirtPlugin::Utils::LoadFileToStructWithRetry(m_metaRepoHandler, preSnapFile, preSnapshotInfo) != SUCCESS) {
            INFOLOG("Load pre snapshot info failed, path: %s.", preSnapFile.c_str());
            return false;
        }
    }
    VolSnapInfo volSnap;
    for (const auto &curVol : diskList) {
        if (!UpdateVolSnapInfo(curVol, preSnapshotInfo)) {
            INFOLOG("No vol(%s) in pre snap file, insert it.", curVol.m_diskIdentifier.c_str());
            volSnap.m_volUuid = curVol.m_diskIdentifier;
            volSnap.m_snapshotWwn = curVol.m_path;
            preSnapshotInfo.m_volSnapList.push_back(volSnap);
        }
    }
    std::string preSnapInfoStr;
    if (!Module::JsonHelper::StructToJsonString(preSnapshotInfo, preSnapInfoStr)) {
        ERRLOG("Convert struct to string failed.");
        return false;
    }
    if (VirtPlugin::Utils::SaveToFileWithRetry(m_metaRepoHandler, preSnapFile, preSnapInfoStr) != SUCCESS) {
        ERRLOG("SaveToFileWithRetry failed.");
        return false;
    }
    return true;
}

/**
 * @brief 检查和更新虚拟机磁盘对应的快照文件
 * 1. 全量备份时，将当前卷的磁盘路径保存到preSnapShotInfo文件中
 * 2. 增量备份时，若当前卷的磁盘路径与preSnapShotInfo文件中当前磁盘的路径不同时(有可能在上次备份完成后有人为创建检查点)，
 *    更新preSnapShotInfo中的磁盘路径为当前路径
 * @return true
 * @return false
 */
bool HyperVProtectEngine::CheckAndUpdatePreSnapShotInfo()
{
    std::string preSnapPath = m_metaRepoPath + VIRT_PLUGIN_META_ROOT;
    if (!m_metaRepoHandler->Exists(preSnapPath)) {
        DBGLOG("PreSnapPath not exist, create it.");
        if (!m_metaRepoHandler->CreateDirectory(preSnapPath)) {
            ERRLOG("GetMachineMetadata failed.(%s). %s", preSnapPath.c_str(), m_taskId.c_str());
            return false;
        }
    }
    std::string preSnapFile = m_metaRepoPath + VIRT_PLUGIN_PRE_SNAPSHOT_INFO;
    if (m_metaRepoHandler->Exists(preSnapFile)) {
        INFOLOG("File(%s) already exists.", preSnapFile.c_str());
        return true;
    }
    HyperVPlugin::GetVMVolumesRequest request;
    request.m_vmId = m_backupPara->protectObject.id.c_str();
    request.m_authKey = m_backupPara->protectEnv.auth.authkey;
    request.m_authPwd = m_backupPara->protectEnv.auth.authPwd;
    INFOLOG("GetMachineMetadata id(%s).", request.m_vmId.c_str());
    std::shared_ptr<HyperVPlugin::GetVMVolumesResponse> response = m_psClient.GetVMVolumes(request);
    if (response == nullptr) {
        ERRLOG("GetMachineMetadata failed.");
        return false;
    }
    if (response->m_diskList.empty()) {
        ERRLOG("Get vm(%s, %s) disks failed.", request.m_vmName.c_str(), m_backupPara->protectObject.id.c_str());
        return false;
    }
    return UpdatePreSnapShotInfo(response->m_diskList, preSnapFile);
}

int32_t HyperVProtectEngine::GetVMBootType(VMInfo &vmInfo)
{
    HyperVPlugin::GetVMInfoRequest request;
    request.m_vmId = m_jobHandle->GetApp().id;
    request.m_authKey = m_backupPara->protectEnv.auth.authkey;
    request.m_authPwd = m_backupPara->protectEnv.auth.authPwd;
    std::shared_ptr<HyperVPlugin::GetVMInfoResponse> response = m_psClient.GetVmInfo(request);
    if (response == nullptr) {
        ERRLOG("Get vm(%s) info failed.", request.m_vmId.c_str());
        return FAILED;
    }
    // hyperV的引导选项由generation字段确定
    vmInfo.m_bootType = std::to_string(response->m_generation);
    return SUCCESS;
}

int32_t HyperVProtectEngine::GetVMMetaData(VMInfo &vmInfo)
{
    if (m_machineMetaCached) {
        INFOLOG("Machine metadata cached, %s", m_taskId.c_str());
        vmInfo = m_vmInfo;
        return SUCCESS;
    }
    if (!InitJobPara()) {
        ERRLOG("Init failed.");
        return FAILED;
    }
    HyperVPlugin::GetVMVolumesRequest request;
    if (m_jobHandle->GetJobType() == JobType::BACKUP) {
        request.m_vmId = m_jobHandle->GetApp().id;
        request.m_authKey = m_backupPara->protectEnv.auth.authkey;
        request.m_authPwd = m_backupPara->protectEnv.auth.authPwd;
    } else {
        request.m_vmId = m_restorePara->targetObject.id;
        request.m_authKey = m_restorePara->targetEnv.auth.authkey;
        request.m_authPwd = m_restorePara->targetEnv.auth.authPwd;
    }
    std::shared_ptr<HyperVPlugin::GetVMVolumesResponse> response = m_psClient.GetVMVolumes(request);
    if (response == nullptr) {
        ERRLOG("GetMachineMetadata failed.");
        return FAILED;
    }
    vmInfo.m_name = request.m_vmName;
    VolInfo vmVol;
    for (const auto &volObj : response->m_diskList) {
        if (TransVolumeInfoToVolInfo(volObj, vmVol) != SUCCESS) {
            ERRLOG("TransVolumeInfoToVolInfo failed.");
            return FAILED;
        }
        vmInfo.m_volList.push_back(vmVol);
    }
    if (m_jobHandle->GetJobType() == JobType::BACKUP) {
        vmInfo.m_extendInfo = m_backupPara->protectObject.extendInfo;
        if (GetVMBootType(vmInfo) != SUCCESS) {
            ERRLOG("GetVMBootType failed.");
            return FAILED;
        }
    }
    m_vmInfo = vmInfo;
    m_machineMetaCached = true;
    INFOLOG("ProtectObject extend info %s.", vmInfo.m_extendInfo.c_str());
    return SUCCESS;
}

int32_t HyperVProtectEngine::TransVolumeInfoToVolInfo(const VolumeInfo &volObj, VolInfo &vmVol)
{
    vmVol.m_uuid = volObj.m_diskIdentifier;
    std::istringstream iStrm(volObj.m_size);
    iStrm >> vmVol.m_volSizeInBytes;
    vmVol.m_type = volObj.m_vhdType;   // fix/dynamic
    vmVol.m_volumeType = volObj.m_vhdFormat;  // vhd/vhdx
    transform(vmVol.m_volumeType.begin(), vmVol.m_volumeType.end(), vmVol.m_volumeType.begin(), tolower);
    vmVol.m_supportBackup = (vmVol.m_volumeType != VHDS_TYPE) && (!volObj.m_shared) && (!volObj.m_isPhysicalHardDisk);
    vmVol.m_name = volObj.m_path; // use path to mark name
    INFOLOG("Volume meta(%s, %lld, %s, %s, %s).", vmVol.m_uuid.c_str(), vmVol.m_volSizeInBytes,
        vmVol.m_type.c_str(), vmVol.m_volumeType.c_str(), vmVol.m_name.c_str());
    std::string volExtendInfo;
    VolumeExtendInfo volExtendObj(volObj);
    if (!Module::JsonHelper::StructToJsonString(volExtendObj, volExtendInfo)) {
        ERRLOG("Convert volume info to string failed.");
        return FAILED;
    }
    INFOLOG("Volume(id: %s) extend info %s.", vmVol.m_uuid.c_str(), volExtendInfo.c_str());
    vmVol.m_extendInfo = volExtendInfo;
    return SUCCESS;
}

int32_t HyperVProtectEngine::SaveVMInfoToCache(VMInfo &vmInfo, const std::string &vmFileInCache)
{
    std::string cacheRepoVMPath = m_cacheRepoPath + VIRT_PLUGIN_CACHE_ROOT;
    if (!m_cacheRepoHandler->Exists(cacheRepoVMPath)) {
        DBGLOG("CacheRepoVMPath not exist, create it.");
        if (!m_cacheRepoHandler->CreateDirectory(cacheRepoVMPath)) {
            ERRLOG("GetMachineMetadata failed.(%s). %s", cacheRepoVMPath.c_str(), m_taskId.c_str());
            return FAILED;
        }
    }
    std::string vmInfoStr;
    if (!Module::JsonHelper::StructToJsonString(vmInfo, vmInfoStr)) {
        ERRLOG("Convert vm info to json string failed.");
        return FAILED;
    }
    INFOLOG("VmInfoMetaFile is %s, vmInfoStr is %s.", vmFileInCache.c_str(), vmInfoStr.c_str());
    if (VirtPlugin::Utils::SaveToFileWithRetry(m_cacheRepoHandler, vmFileInCache, vmInfoStr) != SUCCESS) {
        ERRLOG("Save vm info failed.");
        return FAILED;
    }
    INFOLOG("Save vm info success. VM name: %s.", vmInfo.m_name.c_str());
    return SUCCESS;
}

int32_t HyperVProtectEngine::PreHook(const ExecHookParam &para)
{
    LOGGUARD("");
    InitRepoHandler();
    if (!InitJobPara()) {
        ERRLOG("Init para failed.");
        return FAILED;
    }
    if (m_jobHandle->GetJobType() == JobType::BACKUP && para.stage == JobStage::PRE_PREREQUISITE) {
        if (!CheckAndUpdatePreSnapShotInfo()) {
            ERRLOG("CheckAndUpdatePreSnapShotInfo failed.");
            return FAILED;
        }
        std::string vmInfoPath = m_cacheRepoPath + CACHE_VM_INFO_FILE;
        if (m_cacheRepoHandler->Exists(vmInfoPath)) { // 处理进程故障等场景
            INFOLOG("File(%s) already exists.", vmInfoPath.c_str());
            return SUCCESS;
        }
        VMInfo vmInfo;
        if (GetVMMetaData(vmInfo) != SUCCESS) {
            ERRLOG("Get vm info failed.");
            return FAILED;
        }
        if (SaveVMInfoToCache(vmInfo, vmInfoPath) != SUCCESS) {
            ERRLOG("Save vm info to cache failed.");
            return FAILED;
        }
        return SUCCESS;
    }

    // 后置转换检查点
    if (m_jobHandle->GetJobType() == JobType::BACKUP && para.stage == JobStage::POST_JOB) {
        PreHookPostJob();
        return SUCCESS;
    }
    if (para.stage == JobStage::EXECUTE_SUB_JOB) {
        m_reportParam = {
            "virtual_plugin_restore_job_execute_subjob_start_label",
            JobLogLevel::TASK_LOG_INFO,
            SubJobStatus::RUNNING, 0, 0 };
    }
    return SUCCESS;
}

void HyperVProtectEngine::PreHookPostJob()
{
    LOGGUARD("");
    if (!GenerateAndSaveReferPointInfo()) {
        UpdateCurSnapShotInfoWhenConvertRefFail();
        ERRLOG("Gen reference point failed, ignore fail.");
    } else {
        INFOLOG("Gen reference point success.");
    }
    if (!DeletePreReferencePoint()) {
        ERRLOG("Delete pre-referpoint failed, ignore fail.");
    } else {
        INFOLOG("Delete pre-referpoint success.");
    }
    if (!ClearRemainCheckPointOfVM()) {
        ERRLOG("ClearRemainCheckPointOfVM failed, ignore fail.");
    } else {
        INFOLOG("ClearRemainCheckPointOfVM success.");
    }
}

int32_t HyperVProtectEngine::BackupVmFiles()
{
    HyperVPlugin::GetVMInfoRequest request;
    request.m_vmId = m_backupPara->protectObject.id;
    request.m_authKey = m_backupPara->protectEnv.auth.authkey;
    request.m_authPwd = m_backupPara->protectEnv.auth.authPwd;
    INFOLOG("Post Hook Get vm(id: %s) information.", request.m_vmId.c_str());
    std::shared_ptr<HyperVPlugin::GetVMInfoResponse> response = m_psClient.GetVmInfo(request);
    if (response == nullptr) {
        ERRLOG("Get vm(%s) info failed.", request.m_vmId.c_str());
        return FAILED;
    }
    std::string vmconfigLocation = response->m_checkpointFileLocation + "\\Snapshots\\";
    INFOLOG("Configuration is %s.", vmconfigLocation.c_str());
    std::string dstDirPath = m_metaRepoPath + VIRT_VM_BACKUP_FILE_PATH;
    if (!m_metaRepoHandler->IsDirectory(dstDirPath) && !m_metaRepoHandler->CreateDirectory(dstDirPath)) {
        ERRLOG("Create dir(%s) failed.", dstDirPath.c_str());
        return FAILED;
    }
    INFOLOG("Dst dir is %s.", dstDirPath.c_str());
    std::string vmId = request.m_vmId;
    transform(vmId.begin(), vmId.end(), vmId.begin(), toupper);
    std::vector<std::string> fileSuffixs = { ".vmcx", ".vmgs", ".VMRS" };
    for (const auto &suffix : fileSuffixs) {
        std::string copyFilePath = vmconfigLocation + m_snapShotId + suffix;
        INFOLOG("Copy file %s.", copyFilePath.c_str());
        int res = VirtPlugin::Utils::RetryOpWithT<bool>(std::bind(&RepositoryHandler::Exists,
            m_metaRepoHandler, copyFilePath), true, "Exists");
        if (res != SUCCESS && suffix != ".vmgs") {
            WARNLOG("File(%s) does not exist.", copyFilePath.c_str());
            return FAILED;
        }
        std::string dstFilePath = dstDirPath + "\\" + vmId + suffix;
        INFOLOG("Dst path is %s", dstFilePath.c_str());
        CopyFileW(String2WstringByUtf8(copyFilePath).c_str(), String2WstringByUtf8(dstFilePath).c_str(), false);
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::PostHook(const ExecHookParam &para)
{
    LOGGUARD("");
    if (m_jobHandle->GetJobType() == JobType::BACKUP && para.stage == JobStage::PRE_PREREQUISITE) {
        return BackupVmFiles();
    }
    if (m_jobHandle->GetJobType() == JobType::RESTORE &&
        para.stage == JobStage::POST_JOB && para.jobExecRet == SUCCESS) {
        return DeleteOriginVm();
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::CheckRestoreParams()
{
    DBGLOG("Enter.");
    std::string pathParam;
    if (VirtPlugin::Utils::GetStringValueByKeyFromJsonString(m_restorePara->targetObject.extendInfo,
        "restoreTargetPath", pathParam) != SUCCESS) {
        ERRLOG("Get restoreTargetPath from Json(%s) failed.",
            WIPE_SENSITIVE(m_restorePara->targetObject.extendInfo).c_str());
        return FAILED;
    }
    pathParam += "\\" + m_restorePara->targetObject.name;
    if (pathParam.size() > Module::ConfigReader::getInt("HyperVConfig", "PathLenthLimit")) {
        ApplicationLabelType labelParam;
        labelParam.label = "virtual_plugin_hyperv_path_too_long";
        labelParam.level = JobLogLevel::TASK_LOG_ERROR;
        labelParam.params = std::vector<std::string>{pathParam,
            std::to_string(Module::ConfigReader::getInt("HyperVConfig", "PathLenthLimit"))};
        ERRLOG("Restore path(%s) is too long, limit is %d.", pathParam.c_str(),
            Module::ConfigReader::getInt("HyperVConfig", "PathLenthLimit"));
        ReportJobDetail(labelParam);
        return FAILED;
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::CheckBeforeRecover(const VMInfo &vmObj)
{
    LOGGUARD("");
    if (!InitJobPara()) {
        ERRLOG("InitJobPara failed");
        return FAILED;
    }
    if (m_restoreLevel == RestoreLevel::RESTORE_TYPE_DISK) {
        INFOLOG("Disk restore, will not to delete target vm");
        return SUCCESS;
    }
    if (CheckRestoreParams() != SUCCESS) {
        ERRLOG("CheckRestoreParams failed");
        return FAILED;
    }
    std::size_t* startPos = nullptr;
    int base = 10;
    std::string ifDeleteOriginVMStr;
    if (VirtPlugin::Utils::GetStringValueByKeyFromJsonString(m_restorePara->extendInfo,
        "deleteOriginVM", ifDeleteOriginVMStr) != SUCCESS) {
        ERRLOG("GetStringValueByKeyFromJsonString failed");
        return FAILED;
    }
    m_ifDeleteOriginVM = bool(std::stoi(ifDeleteOriginVMStr, startPos, base));
    return SUCCESS;
}

int32_t HyperVProtectEngine::DeleteOriginVm()
{
    std::string delOriginVM;
    VirtPlugin::Utils::GetStringValueByKeyFromJsonString(m_restorePara->extendInfo, DELETE_ORIGIN_VM_KEY, delOriginVM);
    m_ifDeleteOriginVM = (delOriginVM == DELETE_ORIGIN_VM);
    if (!m_ifDeleteOriginVM || m_restorePara->targetObject.id.empty()) {
        INFOLOG("No need to handle, ifDeleteOriginVM:%d, vmid:%s", m_ifDeleteOriginVM,
            m_restorePara->targetObject.id.c_str());
        return SUCCESS;
    }
    if (InitWmiClient() != SUCCESS) {
        ERRLOG("Get wmi client failed");
        return FAILED;
    }
    if (wmiClient->ChangeVMPowerState(m_restorePara->targetObject.id, true) != SUCCESS) {
        ERRLOG("Power off vm: %s failed", m_restorePara->targetObject.id.c_str());
        return FAILED;
    }
    if (wmiClient->DeleteVM(m_restorePara->targetObject.id) != SUCCESS) {
        ERRLOG("DeleteVM: %s failed", m_restorePara->targetObject.id.c_str());
        return FAILED;
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::CheckBeforeBackup()
{
    LOGGUARD("");
    if (!CheckStorageThreshold()) {
        ERRLOG("Check storage threshold failed");
        return FAILED;
    }
    return SUCCESS;
}

double HyperVProtectEngine::GetStorageThresholdLimit()
{
    std::string threshold;
    VirtPlugin::Utils::GetStringValueByKeyFromJsonString(m_backupPara->extendInfo, VirtPlugin::AVAILABLE_CAPACITY_THRESHOLD_KEY,
        threshold);
    if (threshold == "") {
        return VirtPlugin::DEFAULT_STORAGE_THRESHOLD_LIMIT;
    }
    double result = std::strtod(threshold.c_str(), nullptr);
    if (result < VirtPlugin::MIN_STORAGE_THRESHOLD_LIMIT || result > VirtPlugin::MAX_STORAGE_THRESHOLD_LIMIT) {
        return VirtPlugin::DEFAULT_STORAGE_THRESHOLD_LIMIT;
    }
    return result;
}

void HyperVProtectEngine::GetDiskDriverPathSet(std::shared_ptr<HyperVPlugin::GetClusterSharedVolumeResponse> response,
    VMInfo vmInfo, std::set<std::string> &diskDriverPathSet)
{
    for (const auto &volObj : vmInfo.m_volList) {
        if (volObj.m_name == "") {
            continue;
        }
        bool isMountedVolume = false;
        for (const auto &obj : response->m_clusterSharedVolumeList) {
            if (obj.m_friendlyVolumeName == "" ||
                volObj.m_name.find(obj.m_friendlyVolumeName) == std::string::npos) {
                continue;
            }
            diskDriverPathSet.insert(obj.m_friendlyVolumeName);
            isMountedVolume = true;
            break;
        }
        if (isMountedVolume) {
            continue;
        }
        std::string diskDriverPath = volObj.m_name.substr(0, volObj.m_name.find(":") + 1);
        if (diskDriverPath != "") {
            diskDriverPathSet.insert(diskDriverPath);
        }
    }
}

bool HyperVProtectEngine::CheckStorageThreshold()
{
    VMInfo vmInfo;
    if (GetVMMetaData(vmInfo) != SUCCESS) {
        ERRLOG("CheckStorageThreshold %s GetVMMetaData failed.", vmInfo.m_name.c_str());
        return false;
    }
    HyperVPlugin::Auth auth;
    auth.m_authKey = m_backupPara->protectEnv.auth.authkey;
    auth.m_authPwd = m_backupPara->protectEnv.auth.authPwd;
    std::shared_ptr<HyperVPlugin::GetClusterSharedVolumeResponse> response = m_psClient.GetClusterSharedVolume(auth);
    if (response == nullptr) {
        ERRLOG("CheckStorageThreshold GetClusterSharedVolume failed");
        return false;
    }
    std::set<std::string> diskDriverPathSet;
    GetDiskDriverPathSet(response, vmInfo, diskDriverPathSet);
    if (diskDriverPathSet.empty()) {
        INFOLOG("CheckStorageThreshold %s disk is empty.", vmInfo.m_name.c_str());
        return true;
    }
    double threshold = GetStorageThresholdLimit();
    ULARGE_INTEGER totalBytes;
    ULARGE_INTEGER totalFreeBytes;
    for (const auto &driverPath : diskDriverPathSet) {
        if (driverPath == "") {
            continue;
        }
        if (!GetDiskFreeSpaceEx((LPCTSTR)String2wstring(driverPath).c_str(), NULL, &totalBytes, &totalFreeBytes)) {
            DWORD err = GetLastError();
            ERRLOG("CheckStorageThreshold %s GetDiskFreeSpaceEx failed, err: %d.", driverPath.c_str(), err);
            return false;
        }
        double freeSpaceRatio = 100 * double(totalFreeBytes.QuadPart) / double(totalBytes.QuadPart);
        if (freeSpaceRatio < threshold) {
            ERRLOG("CheckStorageThreshold failed, %s(%s): freeSpaceRatio(%lf) less then threshold(%lf)",
                vmInfo.m_name.c_str(), driverPath.c_str(), freeSpaceRatio, threshold);
            ApplicationLabelType labelParam;
            labelParam.label = HYPERV_LACK_OF_SPACE_LABEL;
            labelParam.level = JobLogLevel::TASK_LOG_ERROR;
            labelParam.params = {driverPath, std::to_string(int(freeSpaceRatio)), std::to_string(int(threshold))};
            ReportJobDetail(labelParam);
            return false;
        }
    }
    return true;
}

int32_t HyperVProtectEngine::CreateSnapshot(SnapshotInfo &snapshot, std::string &errCode)
{
    LOGGUARD("");
    /* 更新磁盘列表的快照信息，虚拟化框架会进行判断 */
    VMInfo curVmInfo;
    GetMachineMetadata(curVmInfo);
    // 快照信息以当前未打快照时的状态, 后面转换参考点时再更新快照信息
    VolSnapInfo snap;
    std::string unSupportDisks{};
    for (const auto &vol : curVmInfo.m_volList) {
        if (!vol.m_supportBackup) {
            ERRLOG("Volume(%s) does not support backup, skip it.", vol.m_uuid.c_str());
            unSupportDisks = unSupportDisks + vol.m_name + ",";
            continue;
        }
        snap.m_volUuid = vol.m_uuid; // 当前的磁盘ID
        snap.m_snapshotWwn = vol.m_name; // 当前的磁盘路径
        snap.m_snapshotId = vol.m_uuid;
        snap.m_extendInfo = vol.m_extendInfo;
        INFOLOG("Snap disk path(%s), snap.m_snapshotId(%s).", snap.m_snapshotWwn.c_str(), snap.m_snapshotId.c_str());
        snapshot.m_volSnapList.push_back(snap);
    }
    if (!unSupportDisks.empty()) {
        unSupportDisks.pop_back();
        ApplicationLabelType labelParam;
        labelParam.label = "virtual_plugin_backup_job_vol_not_support";
        labelParam.level = JobLogLevel::TASK_LOG_WARNING;
        labelParam.params = std::vector<std::string>{unSupportDisks};
        ReportJobDetail(labelParam);
        ERRLOG("Disk unsupported");
        return FAILED;
    }
    if (!ClearRemainCheckPointOfVM()) {
        ERRLOG("ClearRemainCheckPointOfVM failed, ignore fail.");
    } else {
        INFOLOG("ClearRemainCheckPointOfVM success.");
    }
    if (CreateVmCheckPoint(snapshot) != SUCCESS) {
        ERRLOG("CreateVmCheckPoint failed.");
        return FAILED;
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::CreateVmCheckPoint(SnapshotInfo &snapshot)
{
    CreateSnapshotResquest request;
    request.m_vmId = m_jobHandle->GetApp().id;
    request.m_consistencyLevel = "CrashConsistent";
    request.m_authKey = m_backupPara->protectEnv.auth.authkey;
    request.m_authPwd = m_backupPara->protectEnv.auth.authPwd;
    std::shared_ptr<HyperVPlugin::CreateSnapshotResponse> response = m_psClient.CreateCheckPoint(request);
    if (response == nullptr) {
        ERRLOG("Create snapshot failed.");
        return FAILED;
    }
    INFOLOG("Snapshot name is %s.", response->m_name.c_str());
    if (response->m_name.empty()) {
        ERRLOG("Create check point failed.");
        return FAILED;
    }
    snapshot.m_vmName = request.m_vmName;
    snapshot.m_moRef = response->m_name; // 快照名称, 用于转参考点
    m_snapShotId = response->m_configurationID;
    transform(m_snapShotId.begin(), m_snapShotId.end(), m_snapShotId.begin(), toupper);
    INFOLOG("Snapshot id(%s), name(%s).", m_snapShotId.c_str(), snapshot.m_moRef.c_str());
    return SUCCESS;
}

int32_t HyperVProtectEngine::DeleteSnapshot(const SnapshotInfo &snapshot)
{
    LOGGUARD("");
    DeleteSnapshotResquest deleteRequest;
    deleteRequest.m_vmId = m_jobHandle->GetApp().id;
    deleteRequest.m_vmName = m_vmInfo.m_name;
    deleteRequest.m_checkPointName = snapshot.m_moRef;
    deleteRequest.m_authKey = m_backupPara->protectEnv.auth.authkey;
    deleteRequest.m_authPwd = m_backupPara->protectEnv.auth.authPwd;

    INFOLOG("Check point(%s, %s).", deleteRequest.m_vmId.c_str(), deleteRequest.m_checkPointName.c_str());

    if (!m_psClient.DeleteSpecificSnapshot(deleteRequest)) {
        ERRLOG("DeleteSpecificSnapshot failed.");
        return FAILED;
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::QuerySnapshotExists(SnapshotInfo &snapshot)
{
    LOGGUARD("");
    snapshot.m_deleted = true;
    return SUCCESS;
}

bool HyperVProtectEngine::InitJobPara()
{
    if (m_backupPara != nullptr && m_restorePara != nullptr) {
        DBGLOG("Initialized, return directly.");
        return true;
    }
    if (m_jobHandle == nullptr || m_jobHandle->GetJobCommonInfo() == nullptr) {
        ERRLOG("Job handler or job common info is null.");
        return false;
    }
    std::shared_ptr<ThriftDataBase> jobInfo = m_jobHandle->GetJobCommonInfo()->GetJobInfo();
    if (m_jobHandle->GetJobType() == JobType::BACKUP) {
        m_backupPara = std::dynamic_pointer_cast<AppProtect::BackupJob>(jobInfo);
        if (m_backupPara == nullptr) {
            ERRLOG("Get backup job parameter failed.");
            return false;
        }
        m_taskId = m_backupPara->jobId;
    } else if (m_jobHandle->GetJobType() == JobType::RESTORE) {
        m_restorePara = std::dynamic_pointer_cast<AppProtect::RestoreJob>(jobInfo);
        if (m_restorePara == nullptr) {
            ERRLOG("Get restore job parameter failed.");
            return false;
        }
        m_taskId = m_restorePara->jobId;
        std::size_t* startPos = nullptr;
        int base = 10;
        Json::Value jobAdvancePara;
        if (!Module::JsonHelper::JsonStringToJsonValue(m_restorePara->extendInfo, jobAdvancePara)) {
            ERRLOG("Convert %s failed", WIPE_SENSITIVE(m_restorePara->extendInfo).c_str());
            return false;
        }
        m_restoreLevel = RestoreLevel(std::stoi(jobAdvancePara["restoreLevel"].asString(), startPos, base));
    } else {
        ERRLOG("Unsupported job type: %d.", static_cast<int>(m_jobHandle->GetJobType()));
        return false;
    }
    return true;
}

int32_t HyperVProtectEngine::GetMachineMetadata(VMInfo &vmInfo)
{
    LOGGUARD("");
    InitRepoHandler();
    std::string vmFileInCache = m_cacheRepoPath + CACHE_VM_INFO_FILE;
    if (VirtPlugin::Utils::LoadFileToStruct(m_cacheRepoHandler, vmFileInCache, vmInfo) != SUCCESS) {
        WARNLOG("Load vm meta from %s failed.", vmFileInCache.c_str());
        return FAILED;
    }
    INFOLOG("Get vm meta data success.");
    return SUCCESS;
}

int32_t HyperVProtectEngine::GetVolumesMetadata(
    const VMInfo &vmInfo, std::unordered_map<std::string, std::string> &volsMetadata)
{
    ERRLOG("not realized, do not use this mothod");
    return SUCCESS;
}

int32_t HyperVProtectEngine::GetVolumeHandler(const VolInfo &volInfo, std::shared_ptr<VolumeHandler> &volHandler)
{
    LOGGUARD("");
    std::shared_ptr<HyperVVolumeHandler> hyperVVolHandler =
        std::make_shared<HyperVVolumeHandler>(GetJobHandle(), volInfo, m_jobId, m_subJobId);
    if (hyperVVolHandler == nullptr) {
        ERRLOG("Empty pointer");
        return FAILED;
    }
    if (hyperVVolHandler->InitializeVolumeInfo() != SUCCESS) {
        ERRLOG("Initialize volume info failed.");
        return FAILED;
    }
    volHandler = hyperVVolHandler;
    return SUCCESS;
}

int32_t HyperVProtectEngine::CreateVolume(const VolInfo &volObj,
    const std::string &volMetaData, const std::string &vmMoRef, const DatastoreInfo &dsInfo, VolInfo &newVol)
{
    ERRLOG("not realized, do not use this mothod");
    return SUCCESS;
}

int32_t HyperVProtectEngine::DetachVolume(const VolInfo &volObj)
{
    ERRLOG("not realized, do not use this mothod");
    return SUCCESS;
}

int32_t HyperVProtectEngine::ExecAttachVolume(const VolInfo &volObj)
{
    if (!InitJobPara()) {
        ERRLOG("InitJobPara failed");
        return FAILED;
    }
    std::string cacheRepoPath;
    if (GetRepoPathByType(RepositoryDataType::CACHE_REPOSITORY, cacheRepoPath) != SUCCESS) {
        ERRLOG("GetRepoPathByType failed");
        return FAILED;
    }
    std::string newVMMetaDataPath = cacheRepoPath + NEW_VM_INFO_FILE;
    std::string vmId;
    if (m_restoreLevel == RestoreLevel::RESTORE_TYPE_DISK) {
        INFOLOG("Disk restore no need attach volume.");
        return SUCCESS;
    } else {
        VMInfo targetVm;
        if (VirtPlugin::Utils::LoadFileToStructWithRetry(m_repoHandler, newVMMetaDataPath, targetVm) != SUCCESS) {
            ERRLOG("Load file %s to struct failed,", newVMMetaDataPath.c_str());
            return FAILED;
        }
        vmId = targetVm.m_uuid;
    }

    Json::Value diskInfoJson;
    if (!Module::JsonHelper::JsonStringToJsonValue(volObj.m_extendInfo, diskInfoJson)) {
        ERRLOG("convert %s failed", WIPE_SENSITIVE(volObj.m_extendInfo).c_str());
        return FAILED;
    }
    VolumeExtendInfo diskInfo;
    if (!Module::JsonHelper::JsonValueToStruct(diskInfoJson, diskInfo)) {
        ERRLOG("convert %s failed", WIPE_SENSITIVE(diskInfoJson.toStyledString()).c_str());
        return FAILED;
    }
    if (InitWmiClient() != SUCCESS) {
        ERRLOG("Get wmi client failed");
        return FAILED;
    }

    if (wmiClient->AttachDiskToVM(diskInfo, vmId) != SUCCESS) {
        ERRLOG("AttachDiskToVM: %s failed", m_restorePara->targetObject.id.c_str());
        return FAILED;
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::AttachVolume(const VolInfo &volObj)
{
    LOGGUARD("");
    int ret = ExecAttachVolume(volObj);
    if (ret != SUCCESS) {
        m_reportArgs = { volObj.m_name.c_str() };
        m_reportParam = {
            HYPERV_ATTACH_VOLUME_FAILED_LABEL,
            JobLogLevel::TASK_LOG_ERROR,
            SubJobStatus::FAILED, 0, 0 };
        ERRLOG("Attach volume failed");
        return ret;
    }
    m_reportArgs = { m_restorePara->targetObject.name };
    m_reportParam = {
        HYPERV_ATTACH_VOLUME_SUCCESS_LABEL,
        JobLogLevel::TASK_LOG_INFO,
        SubJobStatus::RUNNING, 0, 0 };
    return SUCCESS;
}

int32_t HyperVProtectEngine::DeleteVolume(const VolInfo &volObj)
{
    ERRLOG("not realized, do not use this mothod");
    return SUCCESS;
}

int32_t HyperVProtectEngine::ReplaceVolume(const VolInfo &volObj)
{
    ERRLOG("not realized, do not use this mothod");
    return SUCCESS;
}

int32_t HyperVProtectEngine::PrepareVmModifyParam(const VMInfo& vmInfo, VmModifyParam& param)
{
    if (VirtPlugin::Utils::GetStringValueByKeyFromJsonString(m_restorePara->targetObject.extendInfo,
        "restoreTargetPath", param.configFileTgtPath) != SUCCESS) {
        ERRLOG("GetStringValueByKeyFromJsonString failed");
        return FAILED;
    }
    param.configFileTgtPath += "\\" + m_restorePara->targetObject.name;
    INFOLOG("param.configFileTgtPath %s", param.configFileTgtPath.c_str());
    std::string metaPath;
    if (GetRepoPathByType(RepositoryDataType::META_REPOSITORY, metaPath) != SUCCESS) {
        INFOLOG("get metaPath failed");
        return FAILED;
    }
    INFOLOG("metaPath is %s.", metaPath.c_str());
    std::string fileLocation = metaPath + "\\VIRTUAL_PLUGIN_METADATA\\backup_vm_file_dir\\";
    std::string vmIDToupperCase = m_restorePara->targetObject.id;
    std::transform(vmIDToupperCase.begin(), vmIDToupperCase.end(), vmIDToupperCase.begin(), ::toupper);
    param.configFileSrcPath = Module::CMpString::StrReplace(fileLocation + vmIDToupperCase + ".vmcx", "/", "");
    INFOLOG("configFileSrcPath: %s", param.configFileSrcPath.c_str());
    param.snapshotDataRoot = param.configFileTgtPath;
    param.swapFileDataRoot = param.configFileTgtPath;
    std::string powerOnStr;
    std::size_t* startPos = nullptr;
    int base = 10;
    if (VirtPlugin::Utils::GetStringValueByKeyFromJsonString(m_restorePara->targetObject.extendInfo,
        "disableNetwork", powerOnStr) != SUCCESS) {
        ERRLOG("GetBoolValueByKeyFromJsonString failed");
        return FAILED;
    }
    if (!m_repoHandler->Exists(param.configFileSrcPath)) {
        ERRLOG("File %s dont exist.");
        return FAILED;
    }
    param.disableNetwork = bool(std::stoi(powerOnStr, startPos, base));
    param.newVMName = m_restorePara->targetObject.name;
    INFOLOG("create vm with name %s", param.newVMName.c_str());

    return SUCCESS;
}

int32_t HyperVProtectEngine::CreateMachine(VMInfo &vmInfo)
{
    LOGGUARD("");
    if (!InitJobPara()) {
        ERRLOG("InitJobPara failed");
        return FAILED;
    }

    // find the config file path of vm
    VmModifyParam createVMParam;
    if (PrepareVmModifyParam(vmInfo, createVMParam) != SUCCESS) {
        ERRLOG("PrepareVmModifyParam failed");
        return FAILED;
    }
    if (InitWmiClient() != SUCCESS) {
        ERRLOG("Get wmi client failed");
        return FAILED;
    }
    std::string createdVmId;
    std::string errorDetails;
    if (wmiClient->CreateVM(createVMParam, createdVmId, errorDetails) != SUCCESS || createdVmId.empty()) {
        ERRLOG("CreateVM failed, error info: %s", errorDetails.c_str());
        return FAILED;
    }
    INFOLOG("Create vm %s success.", createdVmId.c_str());
    vmInfo.m_uuid = createdVmId;
    AddCluster(createdVmId);
    return SUCCESS;
}

void HyperVProtectEngine::AddCluster(const std::string &vmId)
{
    std::string addToCluster;
    VirtPlugin::Utils::GetStringValueByKeyFromJsonString(m_restorePara->targetObject.extendInfo,
        ADD_TO_CLUSTER_KEY, addToCluster);
    if (addToCluster != ADD_TO_CLUSTER) {
        INFOLOG("AddCluster %s is not needed", vmId.c_str());
        return;
    }
    ApplicationLabelType labelParam;
    labelParam.level = JobLogLevel::TASK_LOG_WARNING;
    labelParam.label = ADD_TO_CLUSTER_FAILED_LABEL;
    try {
        HyperVPlugin::Auth auth;
        auth.m_authKey = m_restorePara->targetEnv.auth.authkey;
        auth.m_authPwd = m_restorePara->targetEnv.auth.authPwd;
        if (m_psClient.IsAddClusterSuccess(auth, vmId)) {
            INFOLOG("AddCluster %s success", vmId.c_str());
            labelParam.level = JobLogLevel::TASK_LOG_INFO;
            labelParam.label = ADD_TO_CLUSTER_SUCCESS_LABEL;
        }
    } catch (const std::exception &e) {
        ERRLOG("AddCluster %s failed, exception: %s.", vmId.c_str(), WIPE_SENSITIVE(e.what()).c_str());
    }
    ReportJobDetail(labelParam);
}

int32_t HyperVProtectEngine::DeleteMachine(const VMInfo &vmInfo)
{
    LOGGUARD("");
    std::string deleteVmId = vmInfo.m_uuid;
    if (deleteVmId.empty()) {
        ERRLOG("DeleteMachine failed, deleteVmId.empty()");
        return FAILED;
    }
    if (InitWmiClient() != SUCCESS) {
        ERRLOG("Get wmi client failed");
        return FAILED;
    }
    if (wmiClient->DeleteVM(deleteVmId) != SUCCESS) {
        ERRLOG("DeleteVM failed");
        return FAILED;
    }
    INFOLOG("DeleteMachine vm %s success.", deleteVmId.c_str());
    return SUCCESS;
}

int32_t HyperVProtectEngine::RenameMachine(const VMInfo &vmInfo, const std::string &newName)
{
    ERRLOG("not realized, do not use this mothod");
    return SUCCESS;
}

int32_t HyperVProtectEngine::ExecPowerOnMachine(const VMInfo &vmInfo)
{
    if (InitWmiClient() != SUCCESS) {
        ERRLOG("Get wmi client failed");
        return FAILED;
    }
    INFOLOG("Power on vm %s", vmInfo.m_uuid.c_str());
    if (wmiClient->ChangeVMPowerState(vmInfo.m_uuid, false) != SUCCESS) {
        ERRLOG("Power on vm %s failed", vmInfo.m_uuid.c_str());
        return FAILED;
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::PowerOnMachine(const VMInfo &vmInfo)
{
    LOGGUARD("");
    int ret = ExecPowerOnMachine(vmInfo);
    m_reportArgs = { vmInfo.m_name.c_str() };
    if (ret != SUCCESS) {
        m_reportParam = {
            HYPERV_POWER_ON_FAILED_LABEL,
            JobLogLevel::TASK_LOG_ERROR,
            SubJobStatus::FAILED, 0, 0 };
        ERRLOG("Power on machine failed");
        return ret;
    }
    m_reportParam = {
        HYPERV_POWER_ON_SUCCESS_LABEL,
        JobLogLevel::TASK_LOG_INFO,
        SubJobStatus::RUNNING, 0, 0 };
    return SUCCESS;
}

int32_t HyperVProtectEngine::InitWmiClient()
{
    if (wmiClient == nullptr) {
        INFOLOG("create wmiClient ptr");
        wmiClient = WMIClient::GetInstance();
        if (wmiClient == nullptr) {
            ERRLOG("Get wmi client failed");
            return FAILED;
        }
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::PowerOffMachine(const VMInfo &vmInfo)
{
    LOGGUARD("");
    if (!InitJobPara()) {
        ERRLOG("InitJobPara failed");
        return FAILED;
    }
    if (m_restorePara->targetObject.id != "") {
        // if targetObj id is not empty, means it is disk restore job
        INFOLOG("jobid: %s, target vm id: %s", m_taskId.c_str(), m_restorePara->targetObject.id.c_str());
        if (InitWmiClient() != SUCCESS) {
            ERRLOG("Get wmi client failed");
            return FAILED;
        }
        if (wmiClient->ChangeVMPowerState(m_restorePara->targetObject.id, true) != SUCCESS) {
            ERRLOG("Power off vm: %s failed", m_restorePara->targetObject.id.c_str());
            return FAILED;
        }
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::CheckProtectEnvConn(const AppProtect::ApplicationEnvironment &env,
    const AppProtect::Application& app, const std::string &vmId)
{
    INFOLOG("Product env(%s) conn check.", vmId.c_str());
    HyperVPlugin::GetVMInfoRequest request;
    request.m_vmId = vmId;
    request.m_authKey = env.auth.authkey;
    request.m_authPwd = env.auth.authPwd;

    std::shared_ptr<HyperVPlugin::GetVMInfoResponse> response = m_psClient.GetVmInfo(request);
    if (response == nullptr) {
        ERRLOG("GetMachineMetadata failed, %s.", vmId.c_str());
        return FAILED;
    }
    if (response->m_id.empty()) {
        ERRLOG("Vm(%s) check env conn failed.", vmId.c_str());
        return FAILED;
    }
    DBGLOG("Product env(%s) is reachable.", vmId.c_str());
    return SUCCESS;
}

/**
 * @brief 判断是否能查询到虚拟机来判断是否可以在本节点执行
 *
 * @param job
 * @return int32_t
 */
int32_t HyperVProtectEngine::AllowBackupInLocalNode(const AppProtect::BackupJob &job, int32_t &errorCode)
{
    LOGGUARD("");
    if (CheckProtectEnvConn(job.protectEnv, job.protectObject, job.protectObject.id) != SUCCESS) {
        errorCode = VIRTPLUGIN_HYPERV_QUERRY_VM_FAILED;
        return FAILED;
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::AllowBackupSubJobInLocalNode(const AppProtect::BackupJob& job,
    const AppProtect::SubJob& subJob, int32_t &errorCode)
{
    LOGGUARD("");
    if (CheckProtectEnvConn(job.protectEnv, job.protectObject, job.protectObject.id) != SUCCESS) {
        errorCode = VIRTPLUGIN_HYPERV_QUERRY_VM_FAILED;
        return FAILED;
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::AllowRestoreInLocalNode(const AppProtect::RestoreJob &job, int32_t &errorCode)
{
    m_taskId = job.jobId;
    INFOLOG("Start AllowRestoreInLocalNode %s.", m_taskId.c_str());
    std::size_t* startPos = nullptr;
    int base = 10;
    Json::Value jobAdvancePara;
    if (!Module::JsonHelper::JsonStringToJsonValue(job.extendInfo, jobAdvancePara)) {
        ERRLOG("Convert %s failed", WIPE_SENSITIVE(job.extendInfo).c_str());
        return FAILED;
    }
    m_restoreLevel = RestoreLevel(std::stoi(jobAdvancePara["restoreLevel"].asString(), startPos, base));
    if (m_restoreLevel == RestoreLevel::RESTORE_TYPE_DISK) {
        return CheckProtectEnvConn(job.targetEnv, job.targetObject, job.targetObject.id);
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::AllowRestoreSubJobInLocalNode(
    const AppProtect::RestoreJob& job, const AppProtect::SubJob& subJob, int32_t &errorCode)
{
    m_taskId = job.jobId;
    INFOLOG("Start AllowRestoreSubJobInLocalNode %s.", m_taskId.c_str());
    std::size_t* startPos = nullptr;
    int base = 10;
    Json::Value jobAdvancePara;
    if (!Module::JsonHelper::JsonStringToJsonValue(job.extendInfo, jobAdvancePara)) {
        ERRLOG("Convert %s failed", WIPE_SENSITIVE(job.extendInfo).c_str());
        return FAILED;
    }
    m_restoreLevel = RestoreLevel(std::stoi(jobAdvancePara["restoreLevel"].asString(), startPos, base));
    if (m_restoreLevel == RestoreLevel::RESTORE_TYPE_DISK) {
        return CheckProtectEnvConn(job.targetEnv, job.targetObject, job.targetObject.id);
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::CheckBackupJobType(const JobTypeParam &jobTypeParam, bool &checkRet)
{
    checkRet = true;
    for (const auto &volSnap : jobTypeParam.m_snapshotInfo.m_volSnapList) {
        if (volSnap.m_extendInfo.empty()) {
            ERRLOG("VolSnap extend info is empty.");
            checkRet = false;
            break;
        }

        Json::Value extendInfo;
        if (!Module::JsonHelper::JsonStringToJsonValue(volSnap.m_extendInfo, extendInfo)) {
            ERRLOG("VolSnap extend info trans failed.");
            checkRet = false;
            break;
        }
        std::string rctId = extendInfo["RctId"].asString();
        if (rctId.empty()) {
            ERRLOG("Vol: %s Rct id is empty.", volSnap.m_volUuid.c_str());
            checkRet = false;
            break;
        }
    }
    return SUCCESS;
}

void HyperVProtectEngine::DiscoverApplications(std::vector<Application> &returnValue, const std::string &appType)
{
    ERRLOG("not realized, do not use this mothod");
    return;
}

void HyperVProtectEngine::CheckApplication(
    ActionResult &returnValue, const ApplicationEnvironment &appEnv, const AppProtect::Application &application)
{
    LOGGUARD("");
    std::string targetType;
    if (GetTargetType(appEnv, targetType) != SUCCESS) {
        ERRLOG("Get targetType failed.");
        return;
    }
    INFOLOG("Get targetType: %s", targetType.c_str());

    HyperVResourceAccess hyperVResourceAccess(appEnv, application);
    if (targetType == TARGET_TYPE_SCVMM) {
        hyperVResourceAccess.CheckSCVMMConnection(returnValue);
    } else if (targetType == TARGET_TYPE_HOST) {
        hyperVResourceAccess.CheckHostConnection(returnValue);
    } else if (targetType == TARGET_TYPE_CLUSTER) {
        hyperVResourceAccess.CheckClusterConnection(returnValue);
    } else {
        ERRLOG("Invalid targetType: %s", targetType.c_str());
        return;
    }
    INFOLOG("Check connection success.");
    return;
}

void HyperVProtectEngine::ListApplicationResource(std::vector<ApplicationResource> &returnValue,
    const ApplicationEnvironment &appEnv, const Application &application, const ApplicationResource &parentResource)
{
    ERRLOG("not realized, do not use this mothod");
    return;
}

void HyperVProtectEngine::ListApplicationResourceV2(ResourceResultByPage &page, const ListResourceRequest &request)
{
    LOGGUARD("");
    DBGLOG("ListApplicationResourceV2 enter. conditions: %s", request.condition.conditions.c_str());
    m_appEnv = request.appEnv;
    Json::Reader conditionReader;
    Json::Value conditionJson;
    if (!conditionReader.parse(request.condition.conditions, conditionJson)) {
        ERRLOG("Failed to read condition.");
        ThrowPluginException(FAILED, SCAN_FAILED);
        return;
    }
    if (!conditionJson.isMember(TARGET_TYPE_KEY)) {
        ERRLOG("No source key targetType.");
        ThrowPluginException(FAILED, SCAN_FAILED);
        return;
    }
    std::string targetType = conditionJson[TARGET_TYPE_KEY].asString();

    if (!conditionJson.isMember(SCAN_REQUEST_ID_KEY)) {
        ERRLOG("No scan request id provided.");
        ThrowPluginException(FAILED, SCAN_FAILED);
        return;
    }
    std::string scanRequestId = conditionJson[SCAN_REQUEST_ID_KEY].asString();

    HyperVResourceAccess hyperVResourceAccess(request.appEnv, request.condition);
    hyperVResourceAccess.SetRequestId(scanRequestId);

    if (conditionJson.isMember(SCAN_REQUEST_KEY_PARENT_NAME) &&
        conditionJson.isMember(SCAN_REQUEST_KEY_PARENT_ID)) {
        std::string parentId = conditionJson[SCAN_REQUEST_KEY_PARENT_ID].asString();
        std::string parentName = conditionJson[SCAN_REQUEST_KEY_PARENT_NAME].asString();
        hyperVResourceAccess.SetIsParentInfoExist(true);
        hyperVResourceAccess.SetParentInfo(parentId, parentName);
    }

    if (targetType.empty() && hyperVResourceAccess.GetDirectoryList(page) != SUCCESS) {
            ERRLOG("Get directory list failed.");
            ThrowPluginException(FAILED, SCAN_FAILED);
            return;
    }
    Application app;
    if (!request.applications.empty()) {
        app = request.applications[0];
    }
    hyperVResourceAccess.SetApplication(app);
    if (!ExecuteAccessMethod(hyperVResourceAccess, page, targetType)) {
        ERRLOG("ExecuteAccessMethod get target type(%s) failed.", targetType.c_str());
        ThrowPluginException(FAILED, SCAN_FAILED);
        return;
    }

    INFOLOG("ListApplicationResourceV2 success, targetType: %s", targetType.c_str());
    return;
}

bool HyperVProtectEngine::ExecuteAccessMethod(HyperVResourceAccess &hyperVResourceAccess, ResourceResultByPage &page,
    const std::string &targetType)
{
    if (targetType == TARGET_TYPE_CLUSTER) {
        if (hyperVResourceAccess.GetClusterList(page) != SUCCESS) {
            ERRLOG("Get cluster list failed.");
            return false;
        }
    } else if (targetType == TARGET_TYPE_HOST) {
        int32_t iRet{0};
        if (m_appEnv.subType == RESOURCE_SUBTYPE_CLUSTER) {
            iRet = hyperVResourceAccess.GetClusterHostList(page);
        } else {
            iRet = hyperVResourceAccess.GetHostList(page);
        }
        if (iRet != SUCCESS) {
            ERRLOG("Get host list failed. Subtype: %s.", m_appEnv.subType.c_str());
            return false;
        }
    } else if (targetType == TARGET_TYPE_VM) {
        if (hyperVResourceAccess.GetVMList(page) != SUCCESS) {
            ERRLOG("Get vm list failed.");
            return false;
        }
    } else if (targetType == TARGET_TYPE_DISK) {
        if (hyperVResourceAccess.GetDiskList(page) != SUCCESS) {
            ERRLOG("Get disk list failed.");
            return false;
        }
    } else if (targetType == TARGET_TYPE_ADAPTER) {
        if (hyperVResourceAccess.GetVMIpAddress(page) != SUCCESS) {
            ERRLOG("Get vm adapter ip list failed.");
            return false;
        }
    }  else {
        ERRLOG("Invalid targetType: %s", targetType.c_str());
        return false;
    }
    return true;
}

void HyperVProtectEngine::DiscoverHostCluster(ApplicationEnvironment &returnEnv, const ApplicationEnvironment &appEnv)
{
    ERRLOG("not realized, do not use this mothod");
    return;
}

void HyperVProtectEngine::DiscoverAppCluster(
    ApplicationEnvironment &returnEnv, const ApplicationEnvironment &appEnv, const Application &application)
{
    ERRLOG("not realized, do not use this mothod");
    return;
}

bool HyperVProtectEngine::CreateNewFile(const VolInfo &copyVol, const std::string& targetFileName)
{
    InitJobPara();
    CreateVHDRequest request;
    request.m_path = targetFileName;
    request.m_size = copyVol.m_volSizeInBytes;
    request.m_type = copyVol.m_type;
    request.m_volumeType = copyVol.m_volumeType;
    request.m_authKey = m_restorePara->targetEnv.auth.authkey;
    request.m_authPwd = m_restorePara->targetEnv.auth.authPwd;
    std::shared_ptr<HyperVPlugin::CreateVHDResponse> response = m_psClient.CreateVHD(request);
    if (response == nullptr) {
        ERRLOG("Create vhd failed");
        return false;
    }
    if (response->m_id.empty()) {
        ERRLOG("Create new disk failed.");
        return false;
    }
    return true;
}

int HyperVProtectEngine::CreateDiskFileWithNameAndPath(const VolInfo &copyVol, const std::string& absPath,
    std::string& realPath)
{
    std::string path;
    std::string fileName;
    // parse file name and path
    INFOLOG("absPath: %s", absPath.c_str());
    size_t pos = absPath.find_last_of('/', absPath.length());
    path = absPath.substr(0, ++pos);
    fileName = absPath.substr(pos);
    INFOLOG("fileName: %s", fileName.c_str());
    // check if dir valid
    DWORD ftyp = GetFileAttributesA(path.c_str());
    std::shared_ptr<RepositoryHandler> repoHandler = std::make_shared<Win32FileSystemHandler>();
    if (repoHandler == nullptr) {
        ERRLOG("Create repository handler failed.");
        return FAILED;
    }
    if (ftyp == INVALID_FILE_ATTRIBUTES && !repoHandler->CreateDirectory(path)) {
        ERRLOG("path: %s, not exist or abnormal", path.c_str());
        return FAILED;
    }
    if (ftyp & FILE_ATTRIBUTE_DIRECTORY == 0) {
        ERRLOG("path: %s, is exists but not a dir", path.c_str());
        return FAILED;
    }
    // check if fileName duplicated in path, if yes, retry with new names with prefixed number
    uint64_t fileIndex = 1;
    realPath = absPath;
    bool dup = false;
    int failCounter = 0;
    do {
        if (!CreateNewFile(copyVol, realPath)) {
            DWORD err = GetLastError();
            ERRLOG("create file %s failed, error: %d", realPath.c_str(), err);
            if (err != ERROR_ALREADY_EXISTS) {
                return FAILED;
            }
            dup = true;
            WARNLOG("Check failed, file name is deplicated, we will give a new name");
            std::string tempFileName = std::to_string(fileIndex) + "_" + fileName;
            fileIndex++;
            realPath = path + tempFileName;
            failCounter++;
            continue;
        }
        INFOLOG("we will use disk filename: %s", realPath.c_str());
        return SUCCESS;
    } while (dup && (failCounter < MAX_RETRY_TIME_10));
    ERRLOG("unexpected switch");
    return FAILED;
}

int HyperVProtectEngine::GenVolPair(VMInfo &vmObj, const VolInfo &copyVol, const ApplicationResource &targetVol,
    VolMatchPairInfo &volPairs)
{
    INFOLOG("Handle disk: %s", copyVol.m_uuid.c_str());
    VolInfo targetVolInPair;
    Json::Value targetVolume;
    if (InitParaAndGetTargetVolume(targetVol, targetVolume) != SUCCESS) {
        ERRLOG("InitParaAndGetTargetVolume failed, %s", m_taskId.c_str());
        return FAILED;
    }
    if (!targetVolume.isMember("uuid") || !targetVolume["uuid"].isString()) {
        ERRLOG("Target volume uuid not exist");
        return FAILED;
    }
    targetVolInPair.m_uuid = targetVolume["uuid"].asString();
    if (m_restoreLevel == RestoreLevel::RESTORE_TYPE_VM) {
        if (CreateNewDisk(copyVol, targetVol, targetVolume, targetVolInPair) != SUCCESS) {
            ERRLOG("Create new disk with copy vol %s failed.", copyVol.m_uuid.c_str());
            return FAILED;
        }
    } else {
        if (GetVolOfVm(vmObj, targetVolInPair) != SUCCESS) {
            ERRLOG("Get vol info failed.");
            return FAILED;
        }
    }
    volPairs.AddVolPair(copyVol, targetVolInPair);
    return SUCCESS;
}

int32_t HyperVProtectEngine::GetVolOfVm(VMInfo &vmInfo, VolInfo &volInfo)
{
    GetVMMetaData(vmInfo);
    for (const auto &vol : vmInfo.m_volList) {
        if (vol.m_uuid == volInfo.m_uuid) {
            INFOLOG("Get volInfo %s of vm %s success.", volInfo.m_uuid.c_str(), vmInfo.m_uuid.c_str());
            volInfo = vol;
            return SUCCESS;
        }
    }
    ERRLOG("Get volInfo %s of vm %s failed.", volInfo.m_uuid.c_str(), vmInfo.m_uuid.c_str());
    return FAILED;
}

int32_t HyperVProtectEngine::InitParaAndGetTargetVolume(const ApplicationResource &targetVol,
    Json::Value &targetVolume)
{
    std::string targetVolInfoExtendStr;
    if (VirtPlugin::Utils::GetStringValueByKeyFromJsonString(targetVol.extendInfo, "TargetDisk",
        targetVolInfoExtendStr) != SUCCESS) {
        ERRLOG("GetJsonValueByKeyFromJsonString failed: %s", targetVol.extendInfo.c_str());
        return FAILED;
    }
    if (!Module::JsonHelper::JsonStringToJsonValue(targetVolInfoExtendStr, targetVolume)) {
        ERRLOG("Trans json value advanceparams failed");
        return FAILED;
    }
    INFOLOG("Init jobPara and repoHandle success, get targetVolume success");
    return SUCCESS;
}

int32_t HyperVProtectEngine::CreateNewDisk(const VolInfo &copyVol, const ApplicationResource &targetVol,
    Json::Value &targetDiskJson, VolInfo &targetVolInPair)
{
    if (!targetDiskJson.isMember("extendInfo")) {
        ERRLOG("ExtendInfo not exist");
        return FAILED;
    }
    targetDiskJson = targetDiskJson["extendInfo"];
    std::string targetDiskFilePath;
    if (!targetDiskJson.isMember("Path") || !targetDiskJson["Path"].isString()) {
        ERRLOG("ExtendInfo invalid: %s", targetVol.extendInfo.c_str());
        return FAILED;
    }
    targetDiskFilePath = targetDiskJson["Path"].asString() + "\\" + m_restorePara->targetObject.name + "_Disk";
    std::string targetDiskFileFormat;
    if (!targetDiskJson.isMember("Format") || !targetDiskJson["Format"].isString()) {
        ERRLOG("ExtendInfo invalid: %s", targetVol.extendInfo.c_str());
        return FAILED;
    }
    targetDiskFileFormat = targetDiskJson["Format"].asString();
    std::transform(targetDiskFileFormat.begin(), targetDiskFileFormat.end(), targetDiskFileFormat.begin(), ::tolower);
    // if target disk path exists file with duplicated name, CreateDiskFileWithNameAndPath will create a new file name
    INFOLOG("Disk id: %s, target path: %s", targetVol.id.c_str(), targetDiskFilePath.c_str());
    std::string origindiskfilename = GetHardDiskName(copyVol.m_name);
    if (origindiskfilename == "") {
        ERRLOG("incorrect disk name");
        return FAILED;
    }
    std::string newtargetDiskFilePath = GenerateDiskNameForNewVm(targetDiskFilePath,
        origindiskfilename, targetVol.id, targetDiskFileFormat);
    if (newtargetDiskFilePath == "") {
        ERRLOG("no available disk name");
        return FAILED;
    }
    std::string actuallyFilePath;
    if (CreateDiskFileWithNameAndPath(copyVol, newtargetDiskFilePath, actuallyFilePath) != SUCCESS) {
        ERRLOG("CreateDiskFileWithNameAndPath(%s) failed, actual path(%s).",
            newtargetDiskFilePath.c_str(), actuallyFilePath.c_str());
        ApplicationLabelType labelParam;
        labelParam.label = "virtual_plugin_hyperv_create_disk_fail";
        labelParam.level = JobLogLevel::TASK_LOG_ERROR;
        labelParam.params = std::vector<std::string>{newtargetDiskFilePath};
        ReportJobDetail(labelParam);
        return FAILED;
    }
    targetDiskJson["Path"] = actuallyFilePath;
    Json::FastWriter jsonWriter;
    targetVolInPair.m_extendInfo = jsonWriter.write(targetDiskJson);
    targetVolInPair.m_uuid = targetVol.id;
    INFOLOG("Target disk info: %s", targetVol.extendInfo.c_str());
    return SUCCESS;
}

std::string HyperVProtectEngine::GetHardDiskName(const std::string &originDiskPath)
{
    if (originDiskPath.empty()) {
        ERRLOG("originDiskPath.empty");
        return "";
    }
    std::size_t posOfLastSeperater = originDiskPath.find_last_of('\\');
    std::size_t posOfLastDot = originDiskPath.find_last_of('.');
    if (posOfLastSeperater == std::string::npos) {
        posOfLastSeperater = 0;
    }
    if (posOfLastDot == std::string::npos || (posOfLastDot <= posOfLastSeperater)) {
        posOfLastDot = originDiskPath.size();
    }
    std::string originDiskName = originDiskPath.substr(
        posOfLastSeperater + 1, (posOfLastDot - posOfLastSeperater - 1));
    return originDiskName;
}

std::string HyperVProtectEngine::GenerateDiskNameForNewVm(const std::string &targetPath,
    const std::string &originDiskName, const std::string &uuid,
    const std::string &targetDiskFileFormat)
{
    if (targetPath.empty() || originDiskName.empty() ||
        uuid.empty() || targetDiskFileFormat.empty()) {
        ERRLOG("incorrect filename parts");
        return "";
    }
    m_repoHandler = std::make_shared<Win32FileSystemHandler>();
    if (m_repoHandler == nullptr) {
        ERRLOG("Create repository handler failed.");
        return "";
    }
    std::string newDiskName = targetPath + '/' + originDiskName + "." + targetDiskFileFormat;
    if (!m_repoHandler->Exists(newDiskName)) {
        return newDiskName;
    }
    uint64_t curTimeStamp = std::chrono::system_clock::now().time_since_epoch().count() /
        std::chrono::system_clock::period::den;
    newDiskName = targetPath + '/' + originDiskName + "_" + std::to_string(curTimeStamp) +
        "." + targetDiskFileFormat;
    if (!m_repoHandler->Exists(newDiskName)) {
        return newDiskName;
    }
    newDiskName = targetPath + '/' + originDiskName + "_" + uuid +
        "." + targetDiskFileFormat;
    if (!m_repoHandler->Exists(newDiskName)) {
        return newDiskName;
    }
    newDiskName = targetPath + '/' + originDiskName + "_" + std::to_string(curTimeStamp) +
        "_" + uuid + "." + targetDiskFileFormat;
    if (!m_repoHandler->Exists(newDiskName)) {
        return newDiskName;
    }
    ERRLOG("no available disk name %s", newDiskName.c_str());
    return "";
}

int32_t HyperVProtectEngine::GetSnapshotsOfVolume(const VolInfo &volInfo, std::vector<VolSnapInfo> &snapList)
{
    ERRLOG("not realized, do not use this mothod");
    return SUCCESS;
}

int32_t HyperVProtectEngine::GetTargetType(const ApplicationEnvironment &appEnv, std::string &targetType)
{
    if (!appEnv.extendInfo.empty()) {
        Json::Value extendInfo;
        if (!Module::JsonHelper::JsonStringToJsonValue(appEnv.extendInfo, extendInfo)) {
            ERRLOG("Trans json value extendInfo failed.");
            return FAILED;
        }
        if (extendInfo.isMember("targetType")) {
            targetType = extendInfo["targetType"].asString();
            INFOLOG("GetTargetType targetType: %s", targetType.c_str());
            return SUCCESS;
        }
    }
    return FAILED;
}

void HyperVProtectEngine::ThrowPluginException(const int32_t &code, const std::string &message)
{
    AppProtect::AppProtectPluginException exception;
    exception.__set_code(code);
    exception.__set_message(message);
    throw exception;
}

/**
 * @brief 转换参考点失败后，更新当前的快照信息
 *
 * @return true
 * @return false
 */
bool HyperVProtectEngine::UpdateCurSnapShotInfoWhenConvertRefFail()
{
    VMInfo curVmInfo;
    if (GetVMMetaData(curVmInfo) != SUCCESS) {
        ERRLOG("Get cur vm info failed.");
        return FAILED;
    }
    SnapshotInfo snapshot;
    VolSnapInfo snap;
    for (const auto &vol : curVmInfo.m_volList) {
        snap.m_volUuid = vol.m_uuid; // 当前的磁盘ID
        snap.m_snapshotWwn = vol.m_name; // 当前的磁盘路径
        snap.m_snapshotId = vol.m_uuid;
        snap.m_extendInfo = vol.m_extendInfo;
        INFOLOG("Snap disk path(%s), snap.m_snapshotId(%s).", snap.m_snapshotWwn.c_str(), snap.m_snapshotId.c_str());
        snapshot.m_volSnapList.push_back(snap);
    }
    std::string curSnapInfpStr;
    if (!Module::JsonHelper::StructToJsonString(snapshot, curSnapInfpStr)) {
        ERRLOG("Failed to convert cur snapshot info.");
        return FAILED;
    }
    std::string curSnapFile = m_metaRepoPath + VIRT_PLUGIN_SNAPSHOT_INFO;
    INFOLOG("Update cur snapshot info is  %s.", curSnapInfpStr.c_str());
    if (VirtPlugin::Utils::SaveToFile(m_metaRepoHandler, curSnapFile, curSnapInfpStr) != SUCCESS) {
        ERRLOG("Save cur snap info failed, msg: %s.", curSnapInfpStr.c_str());
        return FAILED;
    }
    return SUCCESS;
}

bool HyperVProtectEngine::GenerateAndSaveReferPointInfo(void)
{
    SnapshotInfo curSnapshotInfo;
    std::string curSnapFile = m_metaRepoPath + VIRT_PLUGIN_SNAPSHOT_INFO;
    if (VirtPlugin::Utils::LoadFileToStructWithRetry(m_metaRepoHandler, curSnapFile, curSnapshotInfo) != SUCCESS) {
        ERRLOG("Load cur snapshot info failed, path: %s.", curSnapFile.c_str());
        return false;
    }
    
    std::shared_ptr<ReferrencePointResponse> response = GenerateReferPoint(curSnapshotInfo);
    if (response == nullptr) {
        ERRLOG("response is null.");
        return false;
    }
    
    INFOLOG("Rct size %d.", response->m_rctList.size());
    std::string result;
    for (auto &virDiskId : response->m_virtualDiskIdentifiers) {
        if (!SplitIdAndGetResult(virDiskId, result)) {
            ERRLOG("Split %s failed.", virDiskId.c_str());
            return false;
        }
        INFOLOG("Virtual disk(%s) split result(%s).", virDiskId.c_str(), result.c_str());
        virDiskId = result;
    }
    int retryCount = Module::ConfigReader::getInt("HyperVConfig", "GenerateReferPointCount");
    int retryInterval = Module::ConfigReader::getInt("HyperVConfig", "GenerateReferPointInterval");
    for (int i = 0; i < retryCount; ++i) {
        if (SaveReferPointInfo(response, curSnapFile, curSnapshotInfo)) {
            INFOLOG("Save refer point info success.");
            return true;
        }
        WARNLOG("Save refer point info failed, retry times: %d.", i);
        VirtPlugin::Utils::SleepSeconds(retryInterval);
    }
    ERRLOG("Save refer point info failed, path: %s.", curSnapFile.c_str());
    return false;
}

bool HyperVProtectEngine::SplitIdAndGetResult(const std::string &splitStr, std::string &result)
{
    std::vector<std::string> strList;
    int splitNormalLength = 5; // disk drive id 通过\\切分后的正常长度大小
    // splitStr eg: Microsoft:122CA600-7768-4890-8106-FA9E28E2487D\1955EAA5-B9ED-4148-A51A-0CAFD3F44F2A\0\0\L
    Module::CMpString::StrSplit(strList, splitStr, '\\');
    if (strList.size() != splitNormalLength) {
        ERRLOG("Split %s failed, size %d.", splitStr.c_str(), strList.size());
        return false;
    }
    result = "";
    int boundry = splitNormalLength - 2;
    for (int index = 0; index < splitNormalLength - 1; index++) {
        result += strList[index];
        if (index != boundry) {
            result += "\\";
        }
    }
    return true;
}

std::shared_ptr<GetVMHardDiskDriveResponse> HyperVProtectEngine::GetVMDiskDriver()
{
    int sleepTime = 20;
    VirtPlugin::Utils::SleepSeconds(sleepTime); // td 后面改成查询判断
    GetVMHardDiskDriveRequest getVMDriveRequest;
    getVMDriveRequest.m_vmId = m_jobHandle->GetApp().id;
    getVMDriveRequest.m_authKey = m_backupPara->protectEnv.auth.authkey;
    getVMDriveRequest.m_authPwd = m_backupPara->protectEnv.auth.authPwd;
    std::shared_ptr<GetVMHardDiskDriveResponse> response = m_psClient.GetVmDrive(getVMDriveRequest);
    if (response == nullptr) {
        ERRLOG("Get vm drive failed.");
        return nullptr;
    }
    std::string result;
    for (auto &driverObj : response->m_vmDrivers) {
        if (!SplitIdAndGetResult(driverObj.m_id, result)) {
            ERRLOG("Split %s failed.", driverObj.m_id.c_str());
            return nullptr;
        }
        INFOLOG("Driver infomation(%s, %s, %s).", driverObj.m_id.c_str(), driverObj.m_path.c_str(), result.c_str());
        driverObj.m_id = result;
    }
    return response;
}

bool HyperVProtectEngine::AddRctToVolSnapShotExtendInfo(VolSnapInfo &volSnap,
    std::shared_ptr<ReferrencePointResponse> &response, const std::string &diskId)
{
    Json::Value extendInfo;
    if (!Module::JsonHelper::JsonStringToJsonValue(volSnap.m_extendInfo, extendInfo)) {
        ERRLOG("Trans %s to json value failed", volSnap.m_extendInfo.c_str());
        return false;
    }
    for (int i = 0; i < response->m_virtualDiskIdentifiers.size(); i++) {
        INFOLOG("Virtual disk id %s.", response->m_virtualDiskIdentifiers[i].c_str());
        if (response->m_virtualDiskIdentifiers[i] == diskId) {
            extendInfo["RctId"] = response->m_rctList[i];
            volSnap.m_extendInfo = extendInfo.toStyledString();
            INFOLOG("Disk(%s) extend info %s.", volSnap.m_snapshotWwn.c_str(), volSnap.m_extendInfo.c_str());
            return true;
        }
    }
    return false;
}

bool HyperVProtectEngine::SaveReferPointInfo(std::shared_ptr<ReferrencePointResponse> &response,
    const std::string &curSnapFile, SnapshotInfo &curSnap)
{
    std::shared_ptr<GetVMHardDiskDriveResponse> diskDrives = GetVMDiskDriver();
    if (diskDrives == nullptr) {
        ERRLOG("Get vm driver failed.");
        return false;
    }
    std::string diskId;
    for (int i = 0; i < curSnap.m_volSnapList.size(); i++) {
        auto volSnap = curSnap.m_volSnapList[i];
        diskId = "";
        INFOLOG("Snapshot path(%s).", volSnap.m_snapshotWwn.c_str());
        for (const auto &driver : diskDrives->m_vmDrivers) {
            INFOLOG("Dirver path(%s).", driver.m_path.c_str());
            if (driver.m_path == volSnap.m_snapshotWwn) {
                diskId = driver.m_id;
                break;
            }
        }
        if (diskId.empty()) {
            ERRLOG("Not find drive for %s.", volSnap.m_snapshotWwn.c_str());
            return false;
        }
        if (!AddRctToVolSnapShotExtendInfo(curSnap.m_volSnapList[i], response, diskId)) {
            ERRLOG("Add rct failed.");
            return false;
        }
        curSnap.m_volSnapList[i].m_snapshotId = response->m_instanceId;
        INFOLOG("Disk(%s) extend info(%s).", volSnap.m_snapshotWwn.c_str(),
            curSnap.m_volSnapList[i].m_extendInfo.c_str());
    }
    std::string curSnapInfoStr;
    if (!Module::JsonHelper::StructToJsonString(curSnap, curSnapInfoStr)) {
        ERRLOG("Convert struct to string failed.");
        return false;
    }
    INFOLOG("Save reference point info %s.", curSnapInfoStr.c_str());
    if (VirtPlugin::Utils::SaveToFileWithRetry(m_metaRepoHandler, curSnapFile, curSnapInfoStr) != SUCCESS) {
        ERRLOG("SaveToFileWithRetry failed.");
        return false;
    }

    return true;
}

std::shared_ptr<ReferrencePointResponse> HyperVProtectEngine::GenerateReferPoint(SnapshotInfo &curSnapshotInfo)
{
    HyperVPlugin::ReferrencePointResquest request;
    request.m_vmId = m_jobHandle->GetApp().id;
    request.m_checkPointName = curSnapshotInfo.m_moRef;
    request.m_authKey = m_backupPara->protectEnv.auth.authkey;
    request.m_authPwd = m_backupPara->protectEnv.auth.authPwd;
    INFOLOG("Check point(%s, %s).", request.m_vmId.c_str(), request.m_checkPointName.c_str());
    std::shared_ptr<HyperVPlugin::ReferrencePointResponse> response = m_psClient.ConvertCheckPoint(request);
    if (response == nullptr) {
        ERRLOG("Convert check point failed(%s, %s).", request.m_vmId.c_str(), request.m_checkPointName.c_str());
        return nullptr;
    }
    return response;
}

bool HyperVProtectEngine::DeletePreReferencePoint()
{
    SnapshotInfo curSnapshotInfo;
    std::string curSnapFile = m_metaRepoPath + VIRT_PLUGIN_SNAPSHOT_INFO;
    if (VirtPlugin::Utils::LoadFileToStructWithRetry(m_metaRepoHandler, curSnapFile, curSnapshotInfo) != SUCCESS) {
        ERRLOG("Load cur snapshot info failed, path: %s.", curSnapFile.c_str());
        return false;
    }
    if (curSnapshotInfo.m_volSnapList.empty()) {
        WARNLOG("Cur-snapshot not have volsnap, vmid: %s.", m_jobHandle->GetApp().id.c_str());
        return false;
    }
 
    HyperVPlugin::ReferrencePointResquest request;
    request.m_vmId = m_jobHandle->GetApp().id;
    request.m_instanceId = curSnapshotInfo.m_volSnapList[0].m_snapshotId;
    request.m_authKey = m_backupPara->protectEnv.auth.authkey;
    request.m_authPwd = m_backupPara->protectEnv.auth.authPwd;
    INFOLOG("Refer point(%s, %s).", request.m_vmId.c_str(), request.m_instanceId.c_str());
 
    return m_psClient.DeleteReferPointExcept(request);
}

int32_t HyperVProtectEngine::PrepareFile(std::shared_ptr<RepositoryHandler> &repoHandler,
    const std::string &repoPath, const VolInfo &volInfo)
{
    if (!InitJobPara()) {
        ERRLOG("Init job param failed.");
        return FAILED;
    }
    repoHandler = std::make_shared<VhdHandler>();
    std::string vhdPath = repoPath + "/" + volInfo.m_uuid + "." + volInfo.m_volumeType;
    if (m_jobHandle->GetBackupType() == AppProtect::BackupJobType::FULL_BACKUP) {
        CreateVHDRequest request;
        request.m_path = vhdPath;
        request.m_size = volInfo.m_volSizeInBytes;
        request.m_type = volInfo.m_type;
        request.m_authKey = m_backupPara->protectEnv.auth.authkey;
        request.m_authPwd = m_backupPara->protectEnv.auth.authPwd;
        std::shared_ptr<HyperVPlugin::CreateVHDResponse> response = m_psClient.CreateVHD(request);
        if (response == nullptr) {
            ERRLOG("Create vhd failed");
            return FAILED;
        }
        if (response->m_id.empty()) {
            ERRLOG("Create new disk failed.");
            return FAILED;
        }
    }
    if (repoHandler->Open(vhdPath, "w") == FAILED) {
        ERRLOG("Open %s failed.", vhdPath.c_str());
        return FAILED;
    }
    return SUCCESS;
}

int32_t HyperVProtectEngine::OpenCopyFile(std::shared_ptr<RepositoryHandler> &repoHandler,
    const std::string &repoPath, const VolInfo &volObj)
{
    repoHandler = std::make_shared<VhdHandler>();
    std::string copyFile = repoPath + "/" + volObj.m_uuid + "." + volObj.m_volumeType;
    if (repoHandler->Open(copyFile, "r") != SUCCESS) {
        ERRLOG("Open %s failed.", copyFile.c_str());
        return FAILED;
    }
    INFOLOG("Open copy file success.");
    return SUCCESS;
}

bool HyperVProtectEngine::ClearRemainCheckPointOfVM()
{
    std::string vmId = m_jobHandle->GetApp().id;
    std::string snapName = DEFAULT_CHECKPOINT_PREFIX + vmId;
    INFOLOG("snapName: %s .", snapName.c_str());

    SnapshotInfo snapshot;
    snapshot.m_moRef = snapName;
    
    if (DeleteSnapshot(snapshot) != SUCCESS) {
        ERRLOG("DeleteSnapshot %s failed.", snapName.c_str());
        return false;
    }
    return true;
}

} // namespace HyperVPlugin